Skip to content

feat(RadialProgress): self-animating progress via duration + countdown#33

Merged
chiefcll merged 5 commits into
mainfrom
feat/radial-progress-shader
May 23, 2026
Merged

feat(RadialProgress): self-animating progress via duration + countdown#33
chiefcll merged 5 commits into
mainfrom
feat/radial-progress-shader

Conversation

@chiefcll
Copy link
Copy Markdown
Contributor

Summary

Follow-up to #26. Hooks RadialProgress into the hasShaderTimeFn machinery so the shader can drive its own animation without a JS-side tween.

  • New props: duration (ms per cycle, 0 = disabled) and countdown (0 = fill 0→1, 1 = drain 1→0).
  • When duration > 0: WebGL derives progress from u_time in GLSL; Canvas2D derives it from node.time in render(). time: true on both backends so the stage marks the node as timed and u_time is plumbed automatically.
  • When duration === 0 (default): the existing static progress prop is used — non-animated and JS-animated callers are unchanged.

Why

A countdown ring updating from a JS animation tween costs a CPU-side prop write and a per-frame uniform repush. With duration/countdown, the only per-frame work is requestRender() + one fragment-shader `fract()` — significantly cheaper for the common "always-on circular timer" UI.

API delta

```ts
// before (still works):
node.animate({ shaderProps: { progress: 0 } }, { duration: 2000, loop: true }).start();

// after — equivalent, no JS animation needed:
renderer.createShader('RadialProgress', {
duration: 2000,
countdown: 1, // 1 = drain (countdown), 0 = fill
// ...
});
```

Test plan

  • `pnpm build` clean
  • `pnpm exec vitest run` — 190/190 (4 new for duration resolver + a default-shape check)
  • Visual sanity in `pnpm start`: fill ring and countdown ring both animate smoothly under WebGL.
  • Canvas2D path verified with `?renderMode=canvas`
  • `pnpm start:prod` to confirm transpile-floor compatibility

🤖 Generated with Claude Code

chiefcll and others added 5 commits May 22, 2026 00:04
…tdown UIs

A new sibling to LinearGradient / RadialGradient that draws a stroked ring
filled to a `progress` value, with a gradient swept along the arc, a
background track, and configurable round/butt end caps. Implemented for
both WebGL and Canvas2D backends.

Single-node, one draw call, animatable via `progress: 0..1`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…n props

Add `duration` (ms per cycle) and `countdown` (0 = fill, 1 = drain) props.
When `duration > 0` the shader self-animates: WebGL derives `progress` from
u_time in GLSL, Canvas2D derives it from node.time in render(). `time: true`
on both backends so the stage requests a render each frame and `u_time` is
plumbed automatically — no JS-side animation needed for a smooth loop.

The static `progress` prop still works when `duration === 0` (the default),
so non-animated and JS-animated uses are unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…shader

# Conflicts:
#	examples/tests/shader-radial-progress.ts
#	src/core/shaders/canvas/RadialProgress.ts
#	src/core/shaders/templates/RadialProgressTemplate.test.ts
#	src/core/shaders/templates/RadialProgressTemplate.ts
#	src/core/shaders/webgl/RadialProgress.ts
…PACE

The two self-animating rings now start static (duration: 0) so the example
loads in a calm state. A SPACE keypress toggles both rings between
duration: 0 and duration: 2000. An instruction line and a status label
under the grid document the binding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@chiefcll chiefcll merged commit 632c923 into main May 23, 2026
1 check failed
@chiefcll chiefcll deleted the feat/radial-progress-shader branch May 23, 2026 18:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant